home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Moscow ML 1.31 / source code / mosml / src / mosmllib / PP.sml < prev    next >
Encoding:
Text File  |  1996-07-03  |  20.5 KB  |  615 lines  |  [TEXT/R*ch]

  1. (* PP -- Oppen-style prettyprinters.
  2.  *
  3.  * Modified for Moscow ML from SML/NJ Library version 0.2
  4.  *
  5.  * COPYRIGHT (c) 1992 by AT&T Bell Laboratories.
  6.  * See file mosml/copyrght/copyrght.att for details.
  7.  *)
  8.  
  9. open BasicIO;
  10.  
  11. (* the functions and data for actually doing printing. *)
  12.  
  13. open Array 
  14. infix 9 sub
  15.  
  16. (* the queue library, formerly in unit Ppqueue *)
  17.  
  18. datatype Qend = Qback | Qfront
  19.  
  20. exception QUEUE_FULL
  21. exception QUEUE_EMPTY
  22. exception REQUESTED_QUEUE_SIZE_TOO_SMALL
  23.  
  24. local 
  25.     fun ++ i n = (i + 1) mod n
  26.     fun -- i n = (i - 1) mod n
  27. in
  28.  
  29. abstype 'a queue = QUEUE of {elems: 'a array, (* the contents *)
  30.                              front: int ref,
  31.                              back: int ref,
  32.                              size: int}  (* fixed size of element array *)
  33. with
  34.  
  35.   fun is_empty (QUEUE{front=ref ~1, back=ref ~1,...}) = true
  36.     | is_empty _ = false
  37.  
  38.   fun mk_queue n init_val =
  39.       if (n < 2)
  40.       then raise REQUESTED_QUEUE_SIZE_TOO_SMALL
  41.       else QUEUE{elems=array(n, init_val), front=ref ~1, back=ref ~1, size=n}
  42.  
  43.   fun clear_queue (QUEUE{front,back,...}) = (front := ~1; back := ~1)
  44.  
  45.   fun queue_at Qfront (QUEUE{elems,front,...}) = elems sub !front
  46.     | queue_at Qback (QUEUE{elems,back,...}) = elems sub !back
  47.  
  48.   fun en_queue Qfront item (Q as QUEUE{elems,front,back,size}) =
  49.         if (is_empty Q)
  50.         then (front := 0; back := 0;
  51.               update(elems,0,item))
  52.         else let val i = --(!front) size
  53.              in  if (i = !back)
  54.                  then raise QUEUE_FULL
  55.                  else (update(elems,i,item); front := i)
  56.              end
  57.     | en_queue Qback item (Q as QUEUE{elems,front,back,size}) =
  58.         if (is_empty Q)
  59.         then (front := 0; back := 0;
  60.               update(elems,0,item))
  61.         else let val i = ++(!back) size
  62.              in  if (i = !front)
  63.                  then raise QUEUE_FULL
  64.                  else (update(elems,i,item); back := i)
  65.              end
  66.  
  67.   fun de_queue Qfront (Q as QUEUE{front,back,size,...}) =
  68.         if (!front = !back) (* unitary queue *)
  69.         then clear_queue Q
  70.         else front := ++(!front) size
  71.     | de_queue Qback (Q as QUEUE{front,back,size,...}) =
  72.         if (!front = !back)
  73.         then clear_queue Q
  74.         else back := --(!back) size
  75.  
  76. end (* abstype queue *)
  77. end (* local   *)
  78.  
  79.  
  80. prim_val magic : 'a -> 'b = 1 "identity";
  81.  
  82. (* exception PP_FAIL of string *)
  83.  
  84. datatype break_style = CONSISTENT | INCONSISTENT
  85.  
  86. datatype break_info
  87.   = FITS
  88.   | PACK_ONTO_LINE of int
  89.   | ONE_PER_LINE of int
  90.  
  91. (* Some global values *)
  92. val INFINITY = 999999
  93.  
  94. abstype indent_stack = Istack of break_info list ref
  95. with
  96.   fun mk_indent_stack() = Istack (ref([]:break_info list))
  97.   fun clear_indent_stack (Istack stk) = (stk := ([]:break_info list))
  98.   fun top (Istack stk) =
  99.       case !stk
  100.         of nil => raise Fail "PP-error: top: badly formed block"
  101.      | x::_ => x
  102.   fun push (x,(Istack stk)) = stk := x::(!stk)
  103.   fun pop (Istack stk) =
  104.       case !stk
  105.         of nil => raise Fail "PP-error: pop: badly formed block"
  106.      | _::rest => stk := rest
  107. end
  108.  
  109. (* The delim_stack is used to compute the size of blocks. It is
  110.    a stack of indices into the token buffer. The indices only point to
  111.    BBs, Es, and BRs. We push BBs and Es onto the stack until a BR
  112.    is encountered. Then we compute sizes and pop. When we encounter
  113.    a BR in the middle of a block, we compute the Distance_to_next_break
  114.    of the previous BR in the block, if there was one.
  115.  
  116.    We need to be able to delete from the bottom of the delim_stack, so
  117.    we use a queue, treated with a stack discipline, i.e., we only add
  118.    items at the head of the queue, but can delete from the front or
  119.    back of the queue.
  120. *)
  121. abstype delim_stack = Dstack of int queue
  122. with
  123.   fun new_delim_stack i = Dstack(mk_queue i ~1)
  124.   fun reset_delim_stack (Dstack q) = clear_queue q
  125.  
  126.   fun pop_delim_stack (Dstack d) = de_queue Qfront d
  127.   fun pop_bottom_delim_stack (Dstack d) = de_queue Qback d
  128.  
  129.   fun push_delim_stack(i,Dstack d) = en_queue Qfront i d
  130.   fun top_delim_stack (Dstack d) = queue_at Qfront d
  131.   fun bottom_delim_stack (Dstack d) = queue_at Qback d
  132.   fun delim_stack_is_empty (Dstack d) = is_empty d
  133. end
  134.  
  135.  
  136. type block_info = { Block_size : int ref,
  137.                     Block_offset : int,
  138.                     How_to_indent : break_style }
  139.  
  140.  
  141. (* Distance_to_next_break includes Number_of_blanks. Break_offset is
  142.    a local offset for the break. BB represents a sequence of contiguous
  143.    Begins. E represents a sequence of contiguous Ends.
  144. *)
  145. datatype pp_token
  146.   = S of  {String : string, Length : int}
  147.   | BB of {Pblocks : block_info list ref,   (* Processed   *)
  148.            Ublocks : block_info list ref}  (* Unprocessed *)
  149.   | E of  {Pend : int ref, Uend : int ref}
  150.   | BR of {Distance_to_next_break : int ref,
  151.            Number_of_blanks : int,
  152.            Break_offset : int}
  153.  
  154.  
  155. (* The initial values in the token buffer *)
  156. val initial_token_value = S{String = "", Length = 0}
  157.  
  158. (* type ppstream = General.ppstream; *)
  159. datatype ppstream_ =
  160.   PPS of
  161.      {consumer : string -> unit,
  162.       linewidth : int,
  163.       flush : unit -> unit,
  164.       the_token_buffer : pp_token array,
  165.       the_delim_stack : delim_stack,
  166.       the_indent_stack : indent_stack,
  167.       ++ : int ref -> unit,    (* increment circular buffer index *)
  168.       space_left : int ref,    (* remaining columns on page *)
  169.       left_index : int ref,    (* insertion index *)
  170.       right_index : int ref,   (* output index *)
  171.       left_sum : int ref,      (* size of strings and spaces inserted *)
  172.       right_sum : int ref}     (* size of strings and spaces printed *)
  173.  
  174.  
  175. type ppconsumer = {consumer : string -> unit,
  176.            linewidth : int,
  177.            flush : unit -> unit}
  178.  
  179. fun mk_ppstream {consumer,linewidth,flush} =
  180.     if (linewidth<5)
  181.     then raise Fail "PP-error: linewidth too_small"
  182.     else let val buf_size = 3*linewidth
  183.           in magic(
  184.              PPS{consumer = consumer,
  185.          linewidth = linewidth,
  186.          flush = flush,
  187.          the_token_buffer = array(buf_size, initial_token_value),
  188.          the_delim_stack = new_delim_stack buf_size,
  189.          the_indent_stack = mk_indent_stack (),
  190.          ++ = fn i => i := ((!i + 1) mod buf_size),
  191.          space_left = ref linewidth,
  192.          left_index = ref 0, right_index = ref 0,
  193.          left_sum = ref 0, right_sum = ref 0}
  194.                  ) : ppstream
  195.      end
  196.  
  197. fun dest_ppstream(pps : ppstream) =
  198.   let val PPS{consumer,linewidth,flush, ...} = magic pps
  199.   in {consumer=consumer,linewidth=linewidth,flush=flush} end
  200.  
  201. local
  202.   val space = " "
  203.   fun mk_space (0,s) = String.concat s
  204.     | mk_space (n,s) = mk_space((n-1), (space::s))
  205.   val space_table = Vector.tabulate(100, fn i => mk_space(i,[]))
  206.   fun nspaces n = Vector.sub(space_table, n)
  207.       handle General.Subscript =>
  208.     if n < 0
  209.     then ""
  210.     else let val n2 = n div 2
  211.          val n2_spaces = nspaces n2
  212.          val extra = if (n = (2*n2)) then "" else space
  213.               in String.concat [n2_spaces, n2_spaces, extra]
  214.          end
  215. in
  216.   fun cr_indent (ofn, i) = ofn ("\n"^(nspaces i))
  217.   fun indent (ofn,i) = ofn (nspaces i)
  218. end
  219.  
  220.  
  221. (* Print a the first member of a contiguous sequence of Begins. If there
  222.    are "processed" Begins, then take the first off the list. If there are
  223.    no processed Begins, take the last member off the "unprocessed" list.
  224.    This works because the unprocessed list is treated as a stack, the
  225.    processed list as a FIFO queue. How can an item on the unprocessed list
  226.    be printable? Because of what goes on in add_string. See there for details.
  227. *)
  228.  
  229. fun print_BB (_,{Pblocks = ref [], Ublocks = ref []}) =
  230.              raise Fail "PP-error: print_BB"
  231.   | print_BB (PPS{the_indent_stack,linewidth,space_left=ref sp_left,...},
  232.              {Pblocks as ref({How_to_indent=CONSISTENT,Block_size,
  233.                               Block_offset}::rst),
  234.               Ublocks=ref[]}) =
  235.        (push ((if (!Block_size > sp_left)
  236.                then ONE_PER_LINE (linewidth - (sp_left - Block_offset))
  237.                else FITS),
  238.           the_indent_stack);
  239.         Pblocks := rst)
  240.   | print_BB(PPS{the_indent_stack,linewidth,space_left=ref sp_left,...},
  241.              {Pblocks as ref({Block_size,Block_offset,...}::rst),Ublocks=ref[]}) =
  242.        (push ((if (!Block_size > sp_left)
  243.                then PACK_ONTO_LINE (linewidth - (sp_left - Block_offset))
  244.                else FITS),
  245.           the_indent_stack);
  246.         Pblocks := rst)
  247.   | print_BB (PPS{the_indent_stack, linewidth, space_left=ref sp_left,...},
  248.               {Ublocks,...}) =
  249.       let fun pr_end_Ublock [{How_to_indent=CONSISTENT,Block_size,Block_offset}] l =
  250.         (push ((if (!Block_size > sp_left)
  251.             then ONE_PER_LINE (linewidth - (sp_left - Block_offset))
  252.             else FITS),
  253.                the_indent_stack);
  254.                  List.rev l)
  255.         | pr_end_Ublock [{Block_size,Block_offset,...}] l =
  256.         (push ((if (!Block_size > sp_left)
  257.             then PACK_ONTO_LINE (linewidth - (sp_left - Block_offset))
  258.             else FITS),
  259.                the_indent_stack);
  260.                  List.rev l)
  261.         | pr_end_Ublock (a::rst) l = pr_end_Ublock rst (a::l)
  262.             | pr_end_Ublock _ _ =
  263.                 raise Fail "PP-error: print_BB: internal error"
  264.        in Ublocks := pr_end_Ublock(!Ublocks) []
  265.       end
  266.  
  267.  
  268. (* Uend should always be 0 when print_E is called. *)
  269. fun print_E (_,{Pend = ref 0, Uend = ref 0}) =
  270.       raise Fail "PP-error: print_E"
  271.   | print_E (istack,{Pend, ...}) =
  272.       let fun pop_n_times 0 = ()
  273.         | pop_n_times n = (pop istack; pop_n_times(n-1))
  274.        in pop_n_times(!Pend); Pend := 0
  275.       end
  276.  
  277.  
  278. (* "cursor" is how many spaces across the page we are. *)
  279.  
  280. fun print_token(PPS{consumer,space_left,...}, S{String,Length}) =
  281.       (consumer String;
  282.        space_left := (!space_left) - Length)
  283.   | print_token(ppstrm,BB b) = print_BB(ppstrm,b)
  284.   | print_token(PPS{the_indent_stack,...},E e) =
  285.       print_E (the_indent_stack,e)
  286.   | print_token (PPS{the_indent_stack,space_left,consumer,linewidth,...},
  287.                  BR{Distance_to_next_break,Number_of_blanks,Break_offset}) =
  288.      (case (top the_indent_stack)
  289.         of FITS =>
  290.          (space_left := (!space_left) - Number_of_blanks;
  291.               indent (consumer,Number_of_blanks))
  292.          | (ONE_PER_LINE cursor) =>
  293.              let val new_cursor = cursor + Break_offset
  294.               in space_left := linewidth - new_cursor;
  295.                  cr_indent (consumer,new_cursor)
  296.          end
  297.          | (PACK_ONTO_LINE cursor) =>
  298.          if (!Distance_to_next_break > (!space_left))
  299.          then let val new_cursor = cursor + Break_offset
  300.            in space_left := linewidth - new_cursor;
  301.               cr_indent(consumer,new_cursor)
  302.           end
  303.          else (space_left := !space_left - Number_of_blanks;
  304.            indent (consumer,Number_of_blanks)))
  305.  
  306.  
  307. fun clear_ppstream(pps : ppstream) =
  308.     let val PPS{the_token_buffer, the_delim_stack,
  309.                 the_indent_stack,left_sum, right_sum,
  310.                 left_index, right_index,space_left,linewidth,...}
  311.               = magic pps
  312.         val buf_size = 3*linewidth
  313.     fun set i =
  314.         if (i = buf_size)
  315.         then ()
  316.         else (update(the_token_buffer,i,initial_token_value);
  317.           set (i+1))
  318.      in set 0;
  319.     clear_indent_stack the_indent_stack;
  320.     reset_delim_stack the_delim_stack;
  321.     left_sum := 0; right_sum := 0;
  322.     left_index := 0; right_index := 0;
  323.     space_left := linewidth
  324.     end
  325.  
  326.  
  327. (* Move insertion head to right unless adding a BB and already at a BB,
  328.    or unless adding an E and already at an E.
  329. *)
  330. fun BB_inc_right_index(PPS{the_token_buffer, right_index, ++,...})=
  331.     case (the_token_buffer sub (!right_index))
  332.       of (BB _) => ()
  333.        | _ => ++right_index
  334.  
  335. fun E_inc_right_index(PPS{the_token_buffer,right_index, ++,...})=
  336.     case (the_token_buffer sub (!right_index))
  337.       of (E _) => ()
  338.        | _ => ++right_index
  339.  
  340.  
  341. fun pointers_coincide(PPS{left_index,right_index,the_token_buffer,...}) =
  342.     (!left_index = !right_index) andalso
  343.     (case (the_token_buffer sub (!left_index))
  344.        of (BB {Pblocks = ref [], Ublocks = ref []}) => true
  345.     | (BB _) => false
  346.     | (E {Pend = ref 0, Uend = ref 0}) => true
  347.     | (E _) => false
  348.     | _ => true)
  349.  
  350. fun advance_left (ppstrm as PPS{consumer,left_index,left_sum,
  351.                                 the_token_buffer,++,...},
  352.                   instr) =
  353.     let val NEG = ~1
  354.     val POS = 0
  355.     fun inc_left_sum (BR{Number_of_blanks, ...}) =
  356.          left_sum := (!left_sum) + Number_of_blanks
  357.       | inc_left_sum (S{Length, ...}) = left_sum := (!left_sum) + Length
  358.       | inc_left_sum _ = ()
  359.  
  360.     fun last_size [{Block_size, ...}:block_info] = !Block_size
  361.       | last_size (_::rst) = last_size rst
  362.           | last_size _ = raise Fail "PP-error: last_size: internal error"
  363.     fun token_size (S{Length, ...}) = Length
  364.       | token_size (BB b) =
  365.          (case b
  366.                 of {Pblocks = ref [], Ublocks = ref []} =>
  367.                      raise Fail "PP-error: BB_size"
  368.              | {Pblocks as ref(_::_),Ublocks=ref[]} => POS
  369.          | {Ublocks, ...} => last_size (!Ublocks))
  370.       | token_size (E{Pend = ref 0, Uend = ref 0}) =
  371.               raise Fail "PP-error: token_size.E"
  372.       | token_size (E{Pend = ref 0, ...}) = NEG
  373.       | token_size (E _) = POS
  374.       | token_size (BR {Distance_to_next_break, ...}) = !Distance_to_next_break
  375.     fun loop (instr) =
  376.         if (token_size instr < 0)  (* synchronization point; cannot advance *)
  377.         then ()
  378.         else (print_token(ppstrm,instr);
  379.           inc_left_sum instr;
  380.           if (pointers_coincide ppstrm)
  381.           then ()
  382.           else (* increment left index *)
  383.  
  384.     (* When this is evaluated, we know that the left_index has not yet
  385.        caught up to the right_index. If we are at a BB or an E, we can
  386.        increment left_index if there is no work to be done, i.e., all Begins
  387.        or Ends have been dealt with. Also, we should do some housekeeping and
  388.        clear the buffer at left_index, otherwise we can get errors when
  389.        left_index catches up to right_index and we reset the indices to 0.
  390.        (We might find ourselves adding a BB to an "old" BB, with the result
  391.        that the index is not pushed onto the delim_stack. This can lead to
  392.        mangled output.)
  393.     *)
  394.                (case (the_token_buffer sub (!left_index))
  395.               of (BB {Pblocks = ref [], Ublocks = ref []}) =>
  396.                    (update(the_token_buffer,!left_index,
  397.                        initial_token_value);
  398.                 ++left_index)
  399.                | (BB _) => ()
  400.                | (E {Pend = ref 0, Uend = ref 0}) =>
  401.                    (update(the_token_buffer,!left_index,
  402.                        initial_token_value);
  403.                 ++left_index)
  404.                | (E _) => ()
  405.                | _ => ++left_index;
  406.             loop (the_token_buffer sub (!left_index))))
  407.      in loop instr
  408.     end
  409.  
  410.  
  411. fun begin_block (pps : ppstream) style offset =
  412.   let val ppstrm = magic pps : ppstream_
  413.       val PPS{the_token_buffer, the_delim_stack,left_index,
  414.               left_sum, right_index, right_sum,...}
  415.             = ppstrm
  416.   in
  417.    (if (delim_stack_is_empty the_delim_stack)
  418.     then (left_index := 0;
  419.       left_sum := 1;
  420.       right_index := 0;
  421.       right_sum := 1)
  422.     else BB_inc_right_index ppstrm;
  423.     case (the_token_buffer sub (!right_index))
  424.       of (BB {Ublocks, ...}) =>
  425.        Ublocks := {Block_size = ref (~(!right_sum)),
  426.                Block_offset = offset,
  427.                How_to_indent = style}::(!Ublocks)
  428.        | _ => (update(the_token_buffer, !right_index,
  429.               BB{Pblocks = ref [],
  430.              Ublocks = ref [{Block_size = ref (~(!right_sum)),
  431.                      Block_offset = offset,
  432.                      How_to_indent = style}]});
  433.            push_delim_stack (!right_index, the_delim_stack)))
  434.   end
  435.  
  436. fun end_block(pps : ppstream) =
  437.   let val ppstrm = magic pps : ppstream_
  438.       val PPS{the_token_buffer,the_delim_stack,right_index,...}
  439.             = ppstrm
  440.   in
  441.     if (delim_stack_is_empty the_delim_stack)
  442.     then print_token(ppstrm,(E{Pend = ref 1, Uend = ref 0}))
  443.     else (E_inc_right_index ppstrm;
  444.       case (the_token_buffer sub (!right_index))
  445.             of (E{Uend, ...}) => Uend := !Uend + 1
  446.          | _ => (update(the_token_buffer,!right_index,
  447.                 E{Uend = ref 1, Pend = ref 0});
  448.              push_delim_stack (!right_index, the_delim_stack)))
  449.   end
  450.  
  451. local
  452.   fun check_delim_stack(PPS{the_token_buffer,the_delim_stack,right_sum,...}) =
  453.       let fun check k =
  454.           if (delim_stack_is_empty the_delim_stack)
  455.           then ()
  456.           else case(the_token_buffer sub (top_delim_stack the_delim_stack))
  457.              of (BB{Ublocks as ref ((b as {Block_size, ...})::rst),
  458.                 Pblocks}) =>
  459.                if (k>0)
  460.                then (Block_size := !right_sum + !Block_size;
  461.                  Pblocks := b :: (!Pblocks);
  462.                  Ublocks := rst;
  463.                  if (List.length rst = 0)
  464.                  then pop_delim_stack the_delim_stack
  465.                  else ();
  466.                  check(k-1))
  467.                else ()
  468.               | (E{Pend,Uend}) =>
  469.                (Pend := (!Pend) + (!Uend);
  470.                 Uend := 0;
  471.                 pop_delim_stack the_delim_stack;
  472.                 check(k + !Pend))
  473.               | (BR{Distance_to_next_break, ...}) =>
  474.                (Distance_to_next_break :=
  475.                   !right_sum + !Distance_to_next_break;
  476.                 pop_delim_stack the_delim_stack;
  477.                 if (k>0)
  478.                 then check k
  479.                 else ())
  480.                       | _ => raise Fail "PP-error: check_delim_stack.catchall"
  481.        in check 0
  482.       end
  483. in
  484.  
  485.   fun add_break (pps : ppstream) (n, break_offset) =
  486.     let val ppstrm = magic pps : ppstream_
  487.         val PPS{the_token_buffer,the_delim_stack,left_index,
  488.                 right_index,left_sum,right_sum, ++, ...}
  489.               = ppstrm
  490.     in
  491.       (if (delim_stack_is_empty the_delim_stack)
  492.        then (left_index := 0; right_index := 0;
  493.          left_sum := 1;   right_sum := 1)
  494.        else ++right_index;
  495.        update(the_token_buffer, !right_index,
  496.           BR{Distance_to_next_break = ref (~(!right_sum)),
  497.          Number_of_blanks = n,
  498.          Break_offset = break_offset});
  499.        check_delim_stack ppstrm;
  500.        right_sum := (!right_sum) + n;
  501.        push_delim_stack (!right_index,the_delim_stack))
  502.     end
  503.  
  504.   fun flush_ppstream0(pps : ppstream) =
  505.     let val ppstrm = magic pps : ppstream_
  506.         val PPS{the_delim_stack,the_token_buffer, flush, left_index,...}
  507.               = ppstrm
  508.     in
  509.       (if (delim_stack_is_empty the_delim_stack)
  510.        then ()
  511.        else (check_delim_stack ppstrm;
  512.          advance_left(ppstrm, the_token_buffer sub (!left_index)));
  513.        flush())
  514.     end
  515.  
  516. end (* local *)
  517.  
  518.  
  519. fun flush_ppstream ppstrm =
  520.     (flush_ppstream0 ppstrm;
  521.      clear_ppstream ppstrm)
  522.  
  523. fun add_string (pps : ppstream) s =
  524.     let val ppstrm = magic pps : ppstream_
  525.         val PPS{the_token_buffer,the_delim_stack,consumer,
  526.                 right_index,right_sum,left_sum,
  527.                 left_index,space_left,++,...}
  528.               = ppstrm
  529.         fun fnl [{Block_size, ...}:block_info] = Block_size := INFINITY
  530.       | fnl (_::rst) = fnl rst
  531.           | fnl _ = raise Fail "PP-error: fnl: internal error"
  532.  
  533.     fun set(dstack,BB{Ublocks as ref[{Block_size,...}:block_info],...}) =
  534.           (pop_bottom_delim_stack dstack;
  535.            Block_size := INFINITY)
  536.       | set (_,BB {Ublocks = ref(_::rst), ...}) = fnl rst
  537.       | set (dstack, E{Pend,Uend}) =
  538.           (Pend := (!Pend) + (!Uend);
  539.            Uend := 0;
  540.            pop_bottom_delim_stack dstack)
  541.       | set (dstack,BR{Distance_to_next_break,...}) =
  542.           (pop_bottom_delim_stack dstack;
  543.            Distance_to_next_break := INFINITY)
  544.           | set _ = raise (Fail "PP-error: add_string.set")
  545.  
  546.     fun check_stream () =
  547.         if ((!right_sum - !left_sum) > !space_left)
  548.         then if (delim_stack_is_empty the_delim_stack)
  549.          then ()
  550.          else let val i = bottom_delim_stack the_delim_stack
  551.                in if (!left_index = i)
  552.               then set (the_delim_stack, the_token_buffer sub i)
  553.               else ();
  554.               advance_left(ppstrm,
  555.                                        the_token_buffer sub (!left_index));
  556.                   if (pointers_coincide ppstrm)
  557.                   then ()
  558.                   else check_stream ()
  559.               end
  560.         else ()
  561.  
  562.     val slen = String.size s
  563.     val S_token = S{String = s, Length = slen}
  564.  
  565.     in if (delim_stack_is_empty the_delim_stack)
  566.        then print_token(ppstrm,S_token)
  567.        else (++right_index;
  568.              update(the_token_buffer, !right_index, S_token);
  569.              right_sum := (!right_sum)+slen;
  570.              check_stream ())
  571.    end
  572.  
  573.  
  574. (* Derived form. The +2 is for peace of mind *)
  575. fun add_newline (pps : ppstream) =
  576.   let val PPS{linewidth, ...} = magic pps
  577.   in add_break pps (linewidth+2,0) end
  578.  
  579. (* Derived form. Builds a ppstream, sends pretty printing commands called in
  580.    f to the ppstream, then flushes ppstream.
  581. *)
  582.  
  583. val say = outputc std_out;
  584.  
  585. fun with_pp ppconsumer ppfn =
  586.    let val ppstrm = mk_ppstream ppconsumer
  587.     in ppfn ppstrm;
  588.        flush_ppstream0 ppstrm
  589.    end
  590.    handle Fail msg =>
  591.      (say ">>>> Pretty-printer failure: ";
  592.       say msg;
  593.       say "\n")
  594.  
  595. (*
  596. (* Derived form. Makes an outstream be the target of prettyprinting. *)
  597. fun out_ppstream outstrm n =
  598.     mk_ppstream{consumer = outputc outstrm,
  599.         linewidth = n,
  600.         flush = flush_out outstrm}
  601.  
  602. (* Derived form. Makes a ppstream that observes system parameters. *)
  603. fun mk_std_ppstream() =
  604.     out_ppstream (!Control.Print.out) (!Control.Print.linewidth);
  605.  
  606. *)
  607.  
  608. fun pp_to_string linewidth ppfn ob =
  609.     let val l = ref ([]:string list)
  610.     fun attach s = l := (s::(!l))
  611.      in with_pp {consumer = attach, linewidth=linewidth, flush = fn()=>()}
  612.         (fn ppstrm =>  ppfn ppstrm ob);
  613.     String.concat(List.rev(!l))
  614.     end
  615.